#include "sslclient.h"

#include <QtConcurrent>


#include <stdio.h>
#include <memory.h>
#include <errno.h>
#ifndef	_WIN32
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#else
#include <windows.h>
#endif
#include <pthread.h>

#ifndef ENABLE_GMSSL
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#endif

#include "testfunctions.h"

int	verify_callback(int ok, X509_STORE_CTX *ctx)
{
    printf("verify_callback\n");
    return ok;
}

int	SSL_CTX_use_PrivateKey_file_pass(SSL_CTX *ctx,char *filename,char *pass)
{
#ifndef ENABLE_GMSSL
    EVP_PKEY	*pkey=NULL;
    BIO			*key=NULL;

    key=BIO_new(BIO_s_file());
    BIO_read_filename(key,filename);
    pkey=PEM_read_bio_PrivateKey(key,NULL,NULL,pass);
    if(pkey==NULL)
    {
        printf("PEM_read_bio_PrivateKey err");
        return -1;
    }
    if (SSL_CTX_use_PrivateKey(ctx,pkey) <= 0)
    {
        printf("SSL_CTX_use_PrivateKey err\n");
        return -1;
    }
    BIO_free(key);
#endif
    return 1;
}

#ifndef ENABLE_GMSSL
void* thread_main(void *arg)
{
    int 		err,buflen,read;
    int 		sd;
    SSL_CTX		*ctx=(SSL_CTX *)arg;
    struct 		sockaddr_in dest_sin;
    int		sock;
    SSL			*ssl;
    X509		*server_cert;
    char	   	*str;
    char		buf [1024];
    SSL_METHOD 	*meth;
    FILE		*fp;

    struct sockaddr addr;
    auto* host = gethostbyname("www.baidu.com");
    if(!inet_ntop(host->h_addrtype, host->h_addr, (char *)&addr,sizeof(addr))) {
        printf("inet_ntop err\n");
        return NULL;
    }

    sock = socket(AF_INET, SOCK_STREAM, 0);
    dest_sin.sin_family = AF_INET;
    dest_sin.sin_addr.s_addr = inet_addr( "127.0.0.1" );
    dest_sin.sin_port = htons( 443 );

again:
    err=connect( sock, &addr, sizeof( struct sockaddr));
    if(err<0)
    {
        sleep(1);
        goto again;
    }
    ssl = SSL_new (ctx);
    if(ssl==NULL)
    {
        printf("ss new err\n");
        return NULL;
    }

    SSL_set_fd(ssl,sock);
    err = SSL_connect (ssl);
    if(err<0)
    {
        printf("SSL_connect err\n");
        return NULL;
    }
    printf ("SSL connection using %s\n", SSL_get_cipher (ssl));
#if !defined(USE_CUSTOM_GMSSL)
    server_cert = SSL_get1_peer_certificate (ssl);
#else
    server_cert = SSL_get_peer_certificate (ssl);
#endif
    printf ("Server certificate:\n");
    str = X509_NAME_oneline (X509_get_subject_name (server_cert),0,0);
    printf ("\t subject: %s\n", str);
    OPENSSL_free (str);
    str = X509_NAME_oneline (X509_get_issuer_name  (server_cert),0,0);
    printf ("\t issuer: %s\n", str);
    OPENSSL_free (str);
    X509_free (server_cert);

    err = SSL_write (ssl, "Hello World!", strlen("Hello World!"));
    if(err<0)
    {
        printf("ssl write err\n");
        return NULL;
    }

    SSL_shutdown (ssl);  /* send SSL/TLS close_notify */
    SSL_free (ssl);
    SSL_CTX_free (ctx);
    close(sock);
}
#endif

SSLClient::SSLClient(QObject *parent)
    : QObject{parent}
{
    // InitSSl();
}

SSLClient::~SSLClient()
{
    // SSL_CTX_free (ctx);
}


void SSLClient::Connect()
{
#ifndef ENABLE_GMSSL
    for(i=0;i<MAX_T;i++)
    {
        err=pthread_create(&(pid[i]),NULL,&thread_main,(void *)ctx);
        if(err!=0)
        {
            printf("pthread_create err\n");
            continue;
        }
    }

    for (i=0; i<MAX_T; i++)
    {
        pthread_join(pid[i],NULL);
    }
#endif
}

void SSLClient::TestConnect() {
  connect(this,
          &SSLClient::printMessage,
          this,
          &SSLClient::Print,
          Qt::QueuedConnection);

  QtConcurrent::run([this](){
    for(int i = 0; i < 100; i++) {
      QString msg = "TestConnect: " + QString::number(i);
      Q_EMIT printMessage(msg) ;
    }
  });
}

void SSLClient::InitSSl()
{
#ifndef ENABLE_GMSSL
    SSLeay_add_ssl_algorithms();
    meth = (SSL_METHOD 	*)TLSv1_2_client_method();
    SSL_load_error_strings();
    ctx = SSL_CTX_new (meth);
    if(ctx==NULL)
    {
        printf("ssl ctx new eer\n");
        return ;
    }
#endif
}

void SSLClient::Print(const QString &message) {
  printFormatedMessage(message);
}
